package com.ganinfo.common.api.core;

import com.ganinfo.common.api.constant.ApiCommConstant;
import com.ganinfo.common.exception.ApiException;
import com.xiaoleilu.hutool.date.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import sun.security.provider.MD5;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Date;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 描述：
 *
 */
@Component
public class ApiGatewayHand implements InitializingBean, ApplicationContextAware {
    private static final Logger logger = LoggerFactory.getLogger(ApiGatewayHand.class);

    ApiStore apiSorte;

    final ParameterNameDiscoverer parameterUtil;

    /**
     * 构造器
     */
    public ApiGatewayHand() {
        parameterUtil = new LocalVariableTableParameterNameDiscoverer();
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        apiSorte = new ApiStore(context);
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        apiSorte.loadApiFromSpringBeans();
    }
    /**
     * 具体执行方法
     * @param request
     * @return
     */
    public Object handle(HttpServletRequest request){
        // 系统参数验证
        String params = request.getParameter(ApiCommConstant.PARAMS);
        String method = request.getParameter(ApiCommConstant.METHOD);
        Object result;
        ApiStore.ApiRunnable apiRun = null;
        try {
            apiRun = sysParamsValdate(request);
            logger.info("请求接口={" + method + "} 参数=" + params + "");
            Object[] args = buildParams(apiRun, params, request);
            result = apiRun.run(args);
        } catch (ApiException e) {
            logger.error("调用接口={" + method + "}异常  参数=" + params + "", e);
            result = handleError(e);
        } catch (InvocationTargetException e) {
            logger.error("调用接口={" + method + "}异常  参数=" + params + "", e.getTargetException());
            result = handleError(e.getTargetException());
        } catch (Exception e) {
            logger.error("其他异常", e);
            result = handleError(e);
        }
        return result;
    }


    /**
     * 系统参数校验
     * @param request
     * @return
     * @throws ApiException
     */
    private ApiStore.ApiRunnable sysParamsValdate(HttpServletRequest request) throws ApiException {
        String apiName = request.getParameter(ApiCommConstant.METHOD);
        String json = request.getParameter(ApiCommConstant.PARAMS);
        sign(request);
        ApiStore.ApiRunnable api;
        if (apiName == null || apiName.trim().equals("")) {
            throw new ApiException("调用失败：参数'method'为空");
        } else if (json == null) {
            throw new ApiException( "调用失败：参数'params'为空");
        } else if ((api = apiSorte.findApiRunnable(apiName)) == null) {
            throw new ApiException( "调用失败：指定API不存在，API:" + apiName);
        }
        return api;
    }

    /**
     * 签名验证
     * @param request
     */
    private void sign(HttpServletRequest request) throws ApiException{
        String params = request.getParameter(ApiCommConstant.PARAMS);
        String method = request.getParameter(ApiCommConstant.METHOD);
        String token=request.getParameter(ApiCommConstant.TOKEN);
        String sign=request.getParameter(ApiCommConstant.SIGN);
        String timestamp=request.getParameter(ApiCommConstant.TIMESTAMP);
        if(DateUtil.current(false)-(10*60*1000)>Long.parseLong(timestamp)){
            throw new ApiException( "调用失败：请求已过期");
        }
        //TODO:签名认证
    }

    /***
     * 验证业务参数，和构建业务参数对象
     * @param run
     * @param paramJson
     * @param request
     * @return
     * @throws ApiException
     */
    private Object[] buildParams(ApiStore.ApiRunnable run, String paramJson, HttpServletRequest request)
            throws ApiException {
        Map<String, Object> map = null;
        try {
            map =UtilJson.toMap(paramJson);
        } catch (IllegalArgumentException e) {
            throw new ApiException( "调用失败：json字符串格式异常，请检查params参数 ");
        }
        if (map == null) {
            map = new HashMap<>();
        }

        Method method = run.getTargetMethod();// javassist
        List<String> paramNames = Arrays.asList(parameterUtil.getParameterNames(method));
        // goods ,id
        Class<?>[] paramTypes = method.getParameterTypes(); //反射

        for (Map.Entry<String, Object> m : map.entrySet()) {
            if (!paramNames.contains(m.getKey())) {
                throw new ApiException( "调用失败：接口不存在‘" + m.getKey() + "’参数");
            }
        }
        Object[] args = new Object[paramTypes.length];
        for (int i = 0; i < paramTypes.length; i++) {
            if (paramTypes[i].isAssignableFrom(HttpServletRequest.class)) {
                args[i] = request;
            } else if (map.containsKey(paramNames.get(i))) {
                try {
                    args[i] = convertJsonToBean(map.get(paramNames.get(i)), paramTypes[i]);
                } catch (Exception e) {
                    throw new ApiException( "调用失败：指定参数格式错误或值错误‘" + paramNames.get(i) + "’"
                            + e.getMessage());
                }
            } else {
                args[i] = null;
            }
        }
        return args;
    }


    /**
     * 将MAP转换成具体的目标方方法参数对象
     * @param val
     * @param targetClass
     * @param <T>
     * @return
     * @throws Exception
     */
    private <T> Object convertJsonToBean(Object val, Class<T> targetClass) throws Exception {
        Object result = null;
        if (val == null) {
            return null;
        } else if (Integer.class.equals(targetClass)) {
            result = Integer.parseInt(val.toString());
        } else if (Long.class.equals(targetClass)) {
            result = Long.parseLong(val.toString());
        } else if (Date.class.equals(targetClass)) {
            if (val.toString().matches("[0-9]+")) {
                result = new Date(Long.parseLong(val.toString()));
            } else {
                throw new IllegalArgumentException("日期必须是长整型的时间戳");
            }
        } else if (String.class.equals(targetClass)) {
            if (val instanceof String) {
                result = val;
            } else {
                throw new IllegalArgumentException("转换目标类型为字符串");
            }
        } else {
            result = UtilJson.convertValue(val, targetClass);
        }
        return result;
    }


    private Object handleError(Throwable throwable) {
        String code = "";
        String message = "";

        if (throwable instanceof ApiException) {
            code = "0001";
            message = throwable.getMessage();
        } // 扩展异常规范
        else {
            code = "0002";
            message = throwable.getMessage();
        }

        Map<String, Object> result = new HashMap<>();
        result.put("error", code);
        result.put("msg", message);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PrintStream stream = new PrintStream(out);
        throwable.printStackTrace(stream);
        return result;
    }

}
